<!doctype html>
<html lang=en>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
	<meta name="description" content="">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>J1939GUI</title>
	<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"></script>

	<style>

		body {

			font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;		

		}

		ul {
		  	list-style-type: none;
		}

		.caret {
			  cursor: pointer;
			  -webkit-user-select: none; /* Safari 3.1+ */
			  -moz-user-select: none; /* Firefox 2+ */
			  -ms-user-select: none; /* IE 10+ */
			  user-select: none;
			  font-size: 16px;
		  	
		}

		.caret::before {
			  content: "\25B6";
			  color: black;
			  display: inline-block;
			  margin-right: 6px;
		}


		.nested {
			  display: block;
			  font-size: 14px;
		}


		.win_container {	

			border: 3px solid #F1F1F1;
			border-top-left-radius: 4px;
			border-top-left-radius: 4px;

		}

	
		.win_title {	

			padding: 5px;
			border-top-left-radius: 4px;
			border-top-left-radius: 4px;
			background: #F1F1F1;
			


		}

		td, label {
		
			padding-right: 30px;		

		}

	</style>

</head>
<body>

<div ng-app="J1939Gui" ng-controller="J1939">

	

	<div class="win_container" id=transmission>

		<div class="win_title">
			<div id=tx_text>Transmission</div> 
		</div>


		<select ng-model="selFrame">
			<option ng-repeat="pgn in pgns" value="{{ pgn.pgn }}">{{ pgn.name }} : {{ pgn.pgn | hex | uppercase }}</option>
		</select>

		 <button ng-click="addFrame()" ng-init="count=0">Add frame</button> 

		  <ul>

		    <li ng-repeat="frame in tx_frames">
			<span class="caret" ng-click="showSpns[$index] = !showSpns[$index]">
				<span>{{ frame.name }} ({{ frame.pgn | hex | uppercase }})</span>
				<span>
					Priority: 
					<input type="number" number-mask="" ng-enter="changePrio($index)" ng-click="$event.stopPropagation();" ng-model="frame.priority">
				</span>	
				<span>
					Source: 
					<input type="number" number-mask="" ng-enter="changeSrc($index)" ng-click="$event.stopPropagation();" ng-model="frame.source">
				</span>	
				<span ng-show="frame.dest">
					Dest: 
					<input type="number" number-mask="" ng-enter="changeDest($index)" ng-click="$event.stopPropagation();" ng-model="frame.dest">
				</span>		
				<span>
					Period: 
					<input type="number" number-mask="" ng-enter="changePeriod($index)" ng-click="$event.stopPropagation();" ng-model="frame.period">
				</span>	


				<span ng-repeat="(iface, isChecked) in frame.interfaces">
					{{ iface }}
					<input type="checkbox" ng-click="$event.stopPropagation();" ng-change="ifaceChecked($parent.$index, iface)" ng-model="frame.interfaces[iface]" value="{{ isChecked }}">
				</span>
				<button ng-click="deleteFrame($index, $event)">
					Delete 
					
				</button>	
				
			</span>
			 <ul ng-show="showSpns[$index]" class="nested">
				
				<li  ng-repeat="spn in frame.spns">
					<span>SPN {{ spn.number }} ({{ spn.name }})</span>
					<span ng-switch on="spn.type">
						Value: 
						<span ng-switch-when="1"> <!-- Type Status -->
							<select ng-model="spn.value" ng-change="updateSpn($parent.$parent.$index, $parent.$index)">
								<option ng-repeat="desc in spn.descriptions | filter:emptyOrNull" value="{{$index}}" ng-selected="$index == spn.value">{{ desc }}</option>
							</select>
						</span>
						<span ng-switch-default> <!-- Type String, Numeric or Unknown -->
							<input type="text" ng-enter="updateSpn($parent.$parent.$index, $parent.$index)" ng-model="spn.value">
							<span ng-show="spn.units">{{ spn.units }}</span>
						</span>
					</span>
				
				</li>
			</ul>
			  
		    </li>
		  </ul>

	</div>

	<br>


	 
	<div class="win_container" id="reception">

		<div class="win_title">
			<div id=rx_text>Reception</div> 
		</div>

		<label>PGN: <input ng-model="search_pgn"></label>
		<label>Name: <input ng-model="search_name"></label>
		<label>SPN: <input ng-model="search_spn"></label>

		  <ul>

		    <li ng-repeat="(id, frame) in filterRxFrames(rx_frames, search_pgn, search_name, search_spn)">
			<span class="caret" ng-click="showRxSpns[id] = !showRxSpns[id]">
				<span>{{ frame.name }} ({{ frame.pgn | hex | uppercase }})</span>
				<span>
					Priority: {{ frame.priority }}
				</span>	
				<span>
					Source: {{ frame.source }}
				</span>	
				<span ng-show="frame.dest">
					Dest: {{ frame.dest }}
				</span>	
				<span ng-show="frame.count">
					Count: {{ frame.count }}
				</span>	

			</span>
			 <table ng-show="showRxSpns[id]" class="nested">
				
				<tr ng-repeat="spn in frame.spns | filter:{'number':search_spn}">
					<td>SPN {{ spn.number }} ({{ spn.name }})</td>
					<td ng-switch on="spn.type">
						Value: 
						<span style='color: {{ ($even != $parent.$even) ? "blue" : "red" }};'>
							<span ng-switch-when="1"> <!-- Type Status -->
								{{ spn.descriptions[spn.value] }} ({{spn.value}})
							</span>
							<span ng-switch-default> <!-- Type String, Numeric or Unknown -->
								{{ spn.value }}
							</span>
						</span>
						<span ng-show="spn.units">{{ spn.units }}</span>
					</td>
					<td>
						<button ng-click="showGraph(id, spn.number, spn.name)">Graph</button>
					</td>
				
				</tr>
				
				<tr ng-repeat="dtc in frame.dtcs">
					<td>DTC</td>
					<td>SPN: {{ dtc.spn }}</td>
					<td>FMI: {{ dtc.fmi }}</td>
					<td>OC: {{ dtc.oc }}</td>
				</tr>
				
			</table>
			  
		    </li>
		  </ul>
		
		  <div>Show raw<input type="checkbox" ng-change="show_raw()" ng-model="showRaw"></div>

		  <ul ng-show="showRaw">
			<table>
			    <tr ng-repeat="(id, raw) in rx_raw">
				<td style='font-weight: bold; color: {{ $even ? "blue" : "red" }};'>{{ id | hex }}:</td> <td>{{ raw.data }}</td>
				<td ng-show="raw.count">
					Count: {{ raw.count }}
				</td>	
				  
			    </tr>
			</table>
		  </ul>

		  <button ng-click="reset_rx_frame($event)">
					Reset 
		  </button>

	</div>

</div>

</body>
<script type="text/javascript">

var app = angular.module("J1939Gui", []);


app.directive('ngEnter', function () {
    return function (scope, element, attrs) {
        element.bind("keydown keypress", function (event) {
            if (event.which === 13) {
                scope.$apply(function () {
                    scope.$eval(attrs.ngEnter);
                });
                event.preventDefault();
            }
        });
    };
})

app.filter('hex', function () {
  
  return function(input) {
    return Number(input).toString(16);
  };
});

app.controller('J1939', ['$scope', '$interval', '$filter', function($scope, $interval, $filter) {

	var SPN_TYPES = {
		NUMERIC : 0,
		STATUS : 1,
		STRING : 2
	}
	
	$scope.showSpns = [];
	$scope.showRxSpns = {};
	$scope.rx_frames = {};
	$scope.rx_raw = {};

	$scope.addFrame = function() {
	
		var request = { "command" : "add frame" , "data" : Number($scope.selFrame) };
		var jsonReq = JSON.stringify(request);
		webSocket.send(jsonReq);		

	};

	$scope.changePrio = function(index) {
	
		var request = { "command" : "set frame", "prio" : Number($scope.tx_frames[index].priority), "index" : index};
		var jsonReq = JSON.stringify(request);
		webSocket.send(jsonReq);		

	 };


	$scope.changeSrc = function(index) {

	
		var request = { "command" : "set frame", "src" : Number($scope.tx_frames[index].source), "index" : index};
		var jsonReq = JSON.stringify(request);
		webSocket.send(jsonReq);		

	};


	$scope.changeDest = function(index) {

	
		var request = { "command" : "set frame", "dest" : Number($scope.tx_frames[index].dest), "index" : index};
		var jsonReq = JSON.stringify(request);
		webSocket.send(jsonReq);		

	};


	$scope.changePeriod = function(index) {

	
		var request = { "command" : "set frame", "period" : Number($scope.tx_frames[index].period), "index" : index};
		var jsonReq = JSON.stringify(request);
		webSocket.send(jsonReq);	

	};

	$scope.ifaceChecked = function(index, iface) {
		
		
		var request = { "command" : "set frame", "interface" : {}, "index" : index };
		request.interface[iface] = $scope.tx_frames[index]['interfaces'][iface];

		var jsonReq = JSON.stringify(request);
		webSocket.send(jsonReq);	

	};


	$scope.updateSpn = function(frameIndex, spnIndex) {

		var spn = $scope.tx_frames[frameIndex].spns[spnIndex];
		
		var value = spn.value;

		if(spn.type !== SPN_TYPES.STRING) {
		
			value = Number(value);
			
		}
			
		var request = { "command" : "set frame", "spn" : spn.number, "value" :  value, "index" : frameIndex };
		var jsonReq = JSON.stringify(request);
		webSocket.send(jsonReq);	

	};

	$scope.deleteFrame = function(frameIndex, $event) {
		
		var request = { "command" : "delete frame", "index" : frameIndex };
		var jsonReq = JSON.stringify(request);
		webSocket.send(jsonReq);

		$scope.showSpns.splice(frameIndex, 1);

		$event.stopPropagation();

	}


	$scope.emptyOrNull = function(item) {
		
		return !(item === null || item.length === 0);

	};

	//Reset the reception of frames
	$scope.reset_rx_frame = function($event) {
	
		$scope.rx_frames = {};
			
		var request = { "command" : "reset rx"};
		var jsonReq = JSON.stringify(request);
		webSocket.send(jsonReq);

		$event.stopPropagation();

	}

	//To filter received frames by its PGN, Name or SPN.
	$scope.filterRxFrames = function(rx_frames, search_pgn, search_name, search_spn) {

		var filtered_frames = {};
		
		search_pgn = $filter('lowercase')(search_pgn);
		search_name = $filter('lowercase')(search_name);

		for(key in rx_frames) {

			var pgn = $filter('lowercase')($filter('hex')(rx_frames[key].pgn));
			var name = $filter('lowercase')(rx_frames[key].name);

			if(search_pgn && !pgn.includes(search_pgn))	continue;
			if(search_name && !name.includes(search_name))	continue;


			var spn_filter_pass = false;

			if(search_spn) {
				for(i in rx_frames[key].spns) {
					var spn = rx_frames[key].spns[i];
					if(spn.number.toString().includes(search_spn)) {
						spn_filter_pass = true;
						break;
					}				

				}
			} else {	
				spn_filter_pass = true;
			}


			if(spn_filter_pass) {
			
				filtered_frames[key] = rx_frames[key];

			}

		}

		return filtered_frames;

	}

	//Show undecoded frames?
	$scope.show_raw = function() {

		var request = { "command" : "show raw", "raw" : $scope.showRaw };
		var jsonReq = JSON.stringify(request);
		webSocket.send(jsonReq);

	}

	$scope.showGraph = function(id, spn, name) {

		window.open('graph.html?id=' + id + '&spn=' + spn,'targetWindow',
		   'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=800,height=500');
	}

	var webSocket;

	var hostName = window.location.hostname;

	if(hostName.length == 0) { hostName = "127.0.0.1"; }
    
    	console.log("openWSConnection::Connecting to --> ws://" + hostName + ":8000");
    	try {
		webSocket = new WebSocket("ws://" + hostName + ":8000", "j1939-protocol");

		
		webSocket.onopen = function(openEvent) {

		    	console.log("WebSocket OPEN: " + JSON.stringify(openEvent, null, 4));
			

			var request = { "command" : "list frames" };
			var jsonReq = JSON.stringify(request);
			webSocket.send(jsonReq);

			request  = { "command" : "list interfaces" };
			jsonReq = JSON.stringify(request);
			webSocket.send(jsonReq);


			//Every 200 milliseconds we request the new incoming frames.
			$interval(function(){
				var request = { "command" : "check rx" };
				var jsonReq = JSON.stringify(request);
				webSocket.send(jsonReq);			
		

			}, 200);
		   
		};
		webSocket.onclose = function (closeEvent) {

		    console.log("WebSocket CLOSE: " + JSON.stringify(closeEvent, null, 4));
		    
		};
		webSocket.onerror = function (errorEvent) {
		    console.log("WebSocket ERROR: " + JSON.stringify(errorEvent, null, 4));
		};
		webSocket.onmessage = function (messageEvent) {

		    	var wsMsg = messageEvent.data;
		    	console.log("WebSocket MESSAGE: " + wsMsg);

			var response = JSON.parse(wsMsg);

			//Check if we received new frames
			if(response.command == "check rx") {
			
				$scope.$apply(function(x) { 

					for(id in response.rx) {
						
						//Decoded frames
						if(response.rx[id].hasOwnProperty("frame")) {	
							x.rx_frames[id] = response.rx[id].frame; 						
				
						}

						if(x.rx_frames.hasOwnProperty(id)) {
					
							x.rx_frames[id].count = response.rx[id].count;

						}

						//Raw
						if(response.rx[id].hasOwnProperty("raw")) {	
							x.rx_raw[id] =  { data: response.rx[id].raw }; 						
				
						}

						if(x.rx_raw.hasOwnProperty(id)) {
					
							x.rx_raw[id].count = response.rx[id].count;

						}	
						

					}

					

				});


			}


			if(response.hasOwnProperty("frames")) {

				console.log("Setting frames");
				
				$scope.$apply(function(x) { x.tx_frames = response.frames; });
				
			}

			if(response.command == "list frames") {

				$scope.$apply(function(x) { x.pgns = response.data; });

			} else if (response.command == "add frame"){

				console.log("Adding frame");
				
			} else if (response.command == "list interfaces"){

				console.log("List interfaces");
								
				$scope.$apply(function(x) { x.listedIfaces = response.interfaces; });
			}

		};

		

    	} catch (exception) {
		console.error("Error " + exception.message);
    	}

}]);  


</script>

</html>
